﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Channels;
using System.Threading;
using OnlineGameLibrary.Contract;
using OnlineGameLibrary.MessageEx;
using OnlineGameLibrary.Game;
using OnlineGameLibrary.GameEngine.Game;
using System.Configuration;

namespace OnlineGameLibrary.GameEngine.Service
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,ConcurrencyMode=ConcurrencyMode.Multiple,AddressFilterMode=AddressFilterMode.Any)]
    public class GameRoomService : IGameRoomService
    {
        static GameImporter GameImporter { set; get; }
        static List<GameRoom> GameRooms { set; get; }
        static List<Player> Players { set; get; }
        static GameRoomService()
        {
            Players = new List<Player>();
            GameRooms = new List<GameRoom>();
        }

        public static void InitializeServerBinPath(string serverGameFolderPhysicalPath)
        {
            GameImporter = new GameImporter(serverGameFolderPhysicalPath);
        }

        public static void InitializeClientBinPath(string clientGameFolderPhysicalPath)
        {
            clientComponentsDir = clientGameFolderPhysicalPath;
        }

        Player player;
        static int number;
        public GameRoomService()
        {
            player = new Player(OperationContext
                    .Current.GetCallbackChannel<IGameRoomServiceCallBack>());

            player.PlayerID = "player." + number++;
            player.PlayerState = PlayerState.Ready;

            Players.Add(player);

            OperationContext.Current.Channel.Closed += new EventHandler(Channel_Closed);
            OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted);
        }

        public void UserHeartBeat()
        {
            
        }

        public OperationResult UpdatePlayerState(PlayerState state)
        {
            this.player.PlayerState = state;
            var gameroom = this.GetPlayerGameRoom();
            if (gameroom != null)
            {
                var msg = GameRoomMessageDefines.ROOM_PLAYER_UPDATE.CreateMessage(player);
                BoardcastGameRoomMessage(msg,gameroom);
            }
            else
                return OperationResult.Failed("player haven't enter a room.");
            return OperationResult.Success();
        }

        static string clientComponentsDir;
        public List<GameInfo> GetGameList()
        {
            if (string.IsNullOrEmpty(clientComponentsDir))
                throw new Exception("client dir not initialized");

            var list = (from pair in GameImporter.Games
                        select pair.Value.GetGameInfo()).ToList();
            list.ForEach(gi => gi.GameClientComponentUri = clientComponentsDir + gi.GameClientComponentUri);

            return list.ToList();

            //clientComponentsDir = "http://mog.cloudapp.net/gamebins/";
            //clientComponentsDir = "http://localhost/onlinegameplatform.web/gamebins/";
            //var kksgame = new KakaSongGame.KKSGameCreator();
            //var list = new List<GameInfo>();
            //list.Add(kksgame.GetGameInfo());
            //list.ForEach(gi => gi.GameClientComponentUri = clientComponentsDir + gi.GameClientComponentUri);

            //return list;
        }

        public Player PlayerLogin(string name)
        {
            player.PlayerID = player.PlayerID + "." + name;
            return player;
        }

        void Channel_Faulted(object sender, EventArgs e)
        {
            //this.LeaveGameRoom();
        }

        void Channel_Closed(object sender, EventArgs e)
        {
            this.LeaveGameRoom();
            Players.Remove(player);
        }

        public List<GameRoom> GetGameRooms()
        {
            return GameRooms;
        }

        public GameRoom CreateGameRoom(GameConfig config)
        {
            var gameinfo = GetGameList().First(g=>g.IdentityName==config.GameIdentityName);

            if (GameRooms.FirstOrDefault(g => g.Players.Contains(player)) == null)
            {
                var gameroom = new GameRoom
                    {
                        GameConfig = config,
                        GameState = GameState.Begin,
                        GameId = Guid.NewGuid(),
                        GameInfo = gameinfo
                    };
                GameRooms.Add(gameroom);

                // new room
                var msg = GameRoomMessageDefines.ROOM_NEW.CreateMessage(gameroom);
                BoardcastGameRoomMessage(msg);

                AttendGameRoom(gameroom.GameId);
                return gameroom;
            }
            else
                return null;
        }

        GameRoom GetPlayerGameRoom()
        {
            foreach(var gameroom in GameRooms)
                foreach (var player in gameroom.Players)
                {
                    if (player.PlayerID == this.player.PlayerID)
                        return gameroom;
                }
            return null;
        }

        GameInfo GetGameRoomGameInfo(GameRoom gameroom)
        {
            var gameinfolist = GetGameList();
            return gameinfolist.First(gi => gi.IdentityName == gameroom.GameConfig.GameIdentityName);
        }


        public OperationResult UpdateGameConfig(GameConfig config)
        {
            var gameroom = GetPlayerGameRoom();
            if (gameroom.Players.First().PlayerID==this.player.PlayerID)
            {
                gameroom.GameConfig = config;

                var msg = GameRoomMessageDefines.ROOM_CONFIG_UPDATE.CreateMessage(gameroom.GameConfig);
                BoardcastGameRoomMessage( msg, gameroom);

                return OperationResult.Success();
            }
            else
                return OperationResult.Failed("You are not the manager of this game.");
        }


        static void BoardcastGameRoomMessage(Message msg,GameRoom gameroom=null)
        {
            IList<Player> players=null;
            if (gameroom != null)
                players = gameroom.Players;
            else
                players = GameRoomService.Players;

            var buffer = msg.CreateBufferedCopy(players.Count);
            ThreadPool.QueueUserWorkItem(
                new WaitCallback((o) =>
                {
                    var mybuffer = o as MessageBuffer;
                    foreach (var player in players)
                    {
                        try
                        {
                            var newmsg = mybuffer.CreateMessage();
                            player.SendMessageToPlayer(newmsg);
                        }
                        catch
                        {
                            throw;
                        }
                    }
                }), buffer);
        }

        public GameRoom AttendGameRoom(Guid gameid)
        {
            var gameroom = GetPlayerGameRoom();
            
            if (gameroom == null)
            {
                gameroom = GameRooms.FirstOrDefault(g => g.GameId == gameid);
                if (gameroom != null && gameroom.GameState!=GameState.Running)
                {
                    lock (gameroom)
                    {
                        if (gameroom.Players.Count < GetGameRoomGameInfo(gameroom).MaxPlayerNumber)
                        {
                            // player in initializing state
                            this.player.PlayerState = PlayerState.Initializing;
                            gameroom.Players.Add(this.player);

                            var msg = GameRoomMessageDefines.ROOM_PLAYER_JOIN.CreateMessage(this.player);
                            BoardcastGameRoomMessage(msg, gameroom);

                            return gameroom;
                        }
                        else
                            return null;//"game reached its the max player limitation"
                    }
                }
                else
                    return null;//"game room not exist."
            }
            return null;// you have attended a game.
        }

        public OperationResult LeaveGameRoom()
        {
            var gameroom = GetPlayerGameRoom();
            if (gameroom != null)
            {
                if (gameroom.Game != null)
                    gameroom.Game.OnPlayerLeave(this.player.PlayerID);
                gameroom.Players.Remove(this.player);

                var msg = GameRoomMessageDefines.ROOM_PLAYER_LEAVE.CreateMessage(this.player);
                BoardcastGameRoomMessage( msg,gameroom);

                if (gameroom.Players.Count == 0)
                {
                    GameRooms.Remove(gameroom);

                    // boardcast delete room
                    var msg2 = GameRoomMessageDefines.ROOM_DELETE.CreateMessage(gameroom.GameId);
                    BoardcastGameRoomMessage(msg2);
                }

                return OperationResult.Success();
            }
            return OperationResult.Failed("You haven't attended a game.");
        }

        static void OnGameEnd(GameEndEventArgs args)
        {
            args.GameRoom.Game = null;
            args.GameRoom.GameState = GameState.Begin;

            var msg = GameRoomMessageDefines.ROOM_UPDATE.CreateMessage(args.GameRoom);

            // boardcast scope
            BoardcastGameRoomMessage(msg);
            //BoardcastGameRoomMessage(msg, args.GameRoom);
        }

        public OperationResult GameStart()
        {
            var gameroom = GetPlayerGameRoom();
            if (gameroom != null)
            {
                if (gameroom.Players.FirstOrDefault().PlayerID == this.player.PlayerID)
                {
                    if (GetGameRoomGameInfo(gameroom).MinPlayerNumber <= gameroom.Players.Count)
                    {
                        if (gameroom.Game == null)
                        {
                            if (gameroom.Players.Count(p => p.PlayerState != PlayerState.Ready) == 0)
                            {
                                gameroom.GameState = GameState.Running;

                                var msg0 = GameRoomMessageDefines.ROOM_UPDATE.CreateMessage(gameroom);
                                BoardcastGameRoomMessage(msg0);

                                var msg = GameRoomMessageDefines.ROOM_GAMESTART.CreateMessage(DateTime.Now.AddSeconds(2));
                                BoardcastGameRoomMessage(msg, gameroom);

                                // ready go!
                                Thread.Sleep(TimeSpan.FromSeconds(3));

                                try
                                {
                                    //gameroom.Game = new KakaSongGame.KKSGameCreator().CreateGame(gameroom);
                                    gameroom.Game = GameImporter.CreateGame(gameroom);
                                    
                                    gameroom.Game.BeginRun(OnGameEnd);
                                    return OperationResult.Success();
                                }
                                catch (Exception ex)
                                {
                                    return OperationResult.Failed("game creating error. " + ex);
                                }
                            }
                            else
                                return OperationResult.Failed("some player haven't ready");
                        }
                        else
                            return OperationResult.Failed("game already running.");
                    }
                    else
                        return OperationResult.Failed("not enough players");
                }
                else
                    return OperationResult.Failed("You are not the manager of this game.");
            }
            else
                return OperationResult.Failed("You haven't joined a game room");
        }

        public void SendMessageToGame(Message msg)
        {
            this.player.ReceiveMessageFormService(msg);
        }

        public GameConfig LoadGameConfig()
        {
            var gameroom = GetPlayerGameRoom();
            if (gameroom != null)
                return gameroom.GameConfig;
            else
                return null;
        }

        public List<Player> LoadPlayer()
        {
            var gameroom = GetPlayerGameRoom();
            if (gameroom != null)
                return gameroom.Players.ToList();
            else
                return new List<Player>();
        }


        public bool GameReceiveMessage(Guid GameId, Guid PlayerId, string msg)
        {
            // handle here.
            return true;
        }
    }
}
